package com.bestcem.xm.txtanls.util;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Validator;
import com.alibaba.fastjson.JSON;
import com.bestcem.xm.common.core.constant.Constants;
import com.bestcem.xm.common.core.domain.web.XmResultJson;
import com.bestcem.xm.common.core.uitls.DateUtil;
import com.bestcem.xm.txtanls.grpc.v1.enums.IntervalEnum;
import com.bestcem.xm.txtanls.service.dto.chart.PeriodTimeDTO;
import com.bestcem.xm.txtanls.service.dto.chart.TimeRangeDTO;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

/**
 * 查询工具类
 *
 * @author jy.zhao
 * @version 1.0
 * @date 2021/6/7 14:13
 **/
@Slf4j
public class ChartUtil {

    /**
     * 前端显示的日期格式
     */
    private static final String YEAR_MONTH_DAY_FORMAT = "yyyy/MM/dd";
    private static final String YEAR_MONTH_FORMAT = "yyyy/MM";

    /**
     * 一个季度三个月
     */
    private static final int QUARTER_TO_MONTH_VALUE = 3;

    /**
     * 季度标识符号
     */
    private static final String QUARTER_FLAG = "Q";


    /**
     * 计算上一周期时间
     *
     * @param timeRangeDTO 时间周期
     * @return PeriodTimeDTO
     * @author jy.zhao
     * @date 2021/5/25 15:17
     **/
    public static PeriodTimeDTO getLastPeriodTime(TimeRangeDTO timeRangeDTO) {
        return getLastPeriodTime(timeRangeDTO.getPeriodTime(), timeRangeDTO.getInterval());
    }

    /**
     * 计算上一周期的时间
     *
     * @param curPeriodTime 当前周期时间
     * @param interval      时间间隔
     * @return PeriodTimeDTO
     * @author jy.zhao
     * @date 2021/7/4 20:06
     **/
    public static PeriodTimeDTO getLastPeriodTime(PeriodTimeDTO curPeriodTime, Integer interval) {
        LocalDateTime lastStartTime;
        // 上期结束时间为本期开始时间减去1s
        LocalDateTime lastEndTime = LocalDateTimeUtil.offset(curPeriodTime.getStartTime(), -1, ChronoUnit.SECONDS);
        // 时间间隔。从开始时间开始偏移
        if (interval == IntervalEnum.Interval.YEAR_VALUE) {
            lastStartTime = LocalDateTimeUtil.offset(curPeriodTime.getStartTime(), -1, ChronoUnit.YEARS);
        } else if (interval == IntervalEnum.Interval.QUARTER_VALUE) {
            lastStartTime = LocalDateTimeUtil.offset(curPeriodTime.getStartTime(), -3, ChronoUnit.MONTHS);
        } else if (interval == IntervalEnum.Interval.MONTH_VALUE) {
            lastStartTime = LocalDateTimeUtil.offset(curPeriodTime.getStartTime(), -1, ChronoUnit.MONTHS);
        } else if (interval == IntervalEnum.Interval.WEEK_VALUE) {
            lastStartTime = LocalDateTimeUtil.offset(curPeriodTime.getStartTime(), -7, ChronoUnit.DAYS);
        } else {
            // 天间隔或自定义时间。 计算开始时间与结束时间的间隔，然后偏移具体的秒数
            Duration duration = LocalDateTimeUtil.between(curPeriodTime.getStartTime(), curPeriodTime.getEndTime());
            lastStartTime = LocalDateTimeUtil.offset(curPeriodTime.getStartTime(), -(duration.getSeconds() + 1), ChronoUnit.SECONDS);
        }
        // 封装为实体，返回
        PeriodTimeDTO lastPeriodTime = new PeriodTimeDTO();
        lastPeriodTime.setStartTime(lastStartTime);
        lastPeriodTime.setEndTime(lastEndTime);

        return lastPeriodTime;
    }


    /**
     * 根据开始时间字符串计算得到查询的周期时间
     *
     * @param startTime 周期的开始时间，精确到天.如果不穿，默认昨天时间。如果时间间隔为季度，传递的startTime格式为2021Q1.
     * @param interval  时间间隔枚举。默认为天
     * @return PeriodTimeDTO 周期时间
     * @author jy.zhao
     * @date 2021/8/5 15:22
     **/
    public static PeriodTimeDTO getCurPeriodTime(String startTime, Integer interval) {
        if (Validator.isEmpty(startTime)) {
            return null;
        }
        LocalDateTime periodStartTime = DateUtil.getLocalDateTime(startTime);
        if (Validator.isNull(periodStartTime)) {
            periodStartTime = LocalDateTimeUtil.beginOfDay(LocalDateTimeUtil.offset(DateUtil.getCurrentTime(), -1, ChronoUnit.DAYS));
        }
        // 周期结束时间计算： 例如周，先往后偏移7天，往前偏移1秒
        LocalDateTime periodEndTime;
        if (interval == IntervalEnum.Interval.YEAR_VALUE) {
            periodEndTime = LocalDateTimeUtil.offset(LocalDateTimeUtil.offset(periodStartTime, 1, ChronoUnit.YEARS), -1, ChronoUnit.SECONDS);
        } else if (interval == IntervalEnum.Interval.QUARTER_VALUE) {
            periodEndTime = LocalDateTimeUtil.offset(LocalDateTimeUtil.offset(periodStartTime, 3, ChronoUnit.MONTHS), -1, ChronoUnit.SECONDS);
        } else if (interval == IntervalEnum.Interval.MONTH_VALUE) {
            periodEndTime = LocalDateTimeUtil.offset(LocalDateTimeUtil.offset(periodStartTime, 1, ChronoUnit.MONTHS), -1, ChronoUnit.SECONDS);
        } else if (interval == IntervalEnum.Interval.WEEK_VALUE) {
            periodEndTime = LocalDateTimeUtil.offset(LocalDateTimeUtil.offset(periodStartTime, 7, ChronoUnit.DAYS), -1, ChronoUnit.SECONDS);
        } else if (interval == IntervalEnum.Interval.DAY_VALUE) {
            // 得到开始时间当天的结束时间
            periodEndTime = LocalDateTimeUtil.offset(LocalDateTimeUtil.offset(periodStartTime, 1, ChronoUnit.DAYS), -1, ChronoUnit.SECONDS);
        } else {
            return null;
        }

        // 封装为实体，返回
        PeriodTimeDTO curPeriodTime = new PeriodTimeDTO();
        curPeriodTime.setStartTime(periodStartTime);
        curPeriodTime.setEndTime(periodEndTime);
        return curPeriodTime;
    }

    /**
     * 把周期时间转为字符串
     *
     * @param periodTime 周期时间
     * @param interval   时间间隔
     * @return String
     * @author jy.zhao
     * @date 2021/8/5 22:01
     **/
    public static String formatPeriodTime(PeriodTimeDTO periodTime, Integer interval) {
        String time = "";
        if (Validator.isNull(periodTime) || Validator.isNull(periodTime.getStartTime()) || Validator.isNull(periodTime.getEndTime())) {
            return time;
        }
        LocalDateTime periodStartTime = periodTime.getStartTime();
        if (interval == com.bestcem.xm.txtanls.enums.IntervalEnum.DAY.getIndex()) {
            time = periodStartTime.format(DateTimeFormatter.ofPattern(YEAR_MONTH_DAY_FORMAT));
        } else if (interval == com.bestcem.xm.txtanls.enums.IntervalEnum.WEEK.getIndex()) {
            time = String.join(Constants.HYPHEN, periodStartTime.format(DateTimeFormatter.ofPattern(YEAR_MONTH_DAY_FORMAT)),
                    periodTime.getEndTime().format(DateTimeFormatter.ofPattern(YEAR_MONTH_DAY_FORMAT)));
        } else if (interval == com.bestcem.xm.txtanls.enums.IntervalEnum.MONTH.getIndex()) {
            time = periodStartTime.format(DateTimeFormatter.ofPattern(YEAR_MONTH_FORMAT));
        } else if (interval == com.bestcem.xm.txtanls.enums.IntervalEnum.QUARTER.getIndex()) {
            time = convertQuarterTime(periodStartTime);
        } else if (interval == com.bestcem.xm.txtanls.enums.IntervalEnum.YEAR.getIndex()) {
            time = String.valueOf(periodStartTime.getYear());
        } else if (interval == com.bestcem.xm.txtanls.enums.IntervalEnum.CUSTOM.getIndex()) {
            time = String.join(Constants.HYPHEN, periodStartTime.format(DateTimeFormatter.ofPattern(YEAR_MONTH_DAY_FORMAT)),
                    periodTime.getEndTime().format(DateTimeFormatter.ofPattern(YEAR_MONTH_DAY_FORMAT)));
        }
        return time;
    }

    /**
     * 需要编写测试类，所以把private改为public
     * 格式化时间，转为季度表示
     *
     * @param localDateTime 时间
     * @return 季度字符串 "2020Q3"
     */
    public static String convertQuarterTime(LocalDateTime localDateTime) {
        if (Validator.isNull(localDateTime)) {
            return "";
        }
        int year = localDateTime.getYear();
        int month = localDateTime.getMonthValue() / QUARTER_TO_MONTH_VALUE + 1;
        return year + QUARTER_FLAG + month;
    }

    /**
     * 计算百分比
     *
     * @param current 当前数值
     * @param total   总数
     * @return Integer
     * @author jy.zhao
     * @date 2021/6/7 14:15
     **/
    public static Integer calPercentage(int current, int total) {
        if (total == 0) {
            return 0;
        }
        // Math.round(1.5) = 1, Math.round(-1.5) = -1.但是我们需要返回Math.round(1.5) = 1, Math.round(-1.5) = -2.
        double result = Math.abs(current * 1.0) * 100 / total;
        int res = (int) Math.round(result);
        return current > 0 ? res : -res;
    }

    /**
     * 计算情感环比
     *
     * @param current 本期数据
     * @param last    上期数据
     * @return Integer
     * @author jy.zhao
     * @date 2021/5/25 14:12
     **/
    public static Integer calEmotionRatio(int current, int last) {
        if (current == 0 && last == 0) {
            return 0;
        } else if (current != 0 && last == 0) {
            return 100;
        } else if (current == 0 && last != 0) {
            return -100;
        } else {
            return calPercentage(current - last, last);
        }
    }


    /**
     * 计算百分比,保留两位小数
     *
     * @param current 当前数值
     * @param total   总数
     * @return Integer
     * @author jy.zhao
     * @date 2021/12/7 16:46
     **/
    public static BigDecimal calPercentage2(int current, int total) {
        if (total == 0) {
            return BigDecimal.ZERO;
        }
        return new BigDecimal(current * 100).divide(new BigDecimal(total), 2, RoundingMode.UP);
    }

    /**
     * 计算情感环比
     *
     * @param current 本期数据
     * @param last    上期数据
     * @return BigDecimal
     * @author jy.zhao
     * @date 2021/5/25 14:12
     **/
    public static BigDecimal calEmotionRatio2(int current, int last) {
        if (current == 0 && last == 0) {
            return BigDecimal.ZERO;
        } else if (current != 0 && last == 0) {
            return BigDecimal.valueOf(100);
        } else if (current == 0) {
            return BigDecimal.valueOf(-100);
        } else {
            return calPercentage2(current - last, last);
        }
    }

    /**
     * @param response     excel响应
     * @param errorMessage 错误信息
     * @param modelName    模块名称
     * @author jy.zhao
     * @date 2021/8/12 21:27
     **/
    public static void excelErrorResponse(HttpServletResponse response, Integer errcode, String errorMessage, String modelName) {
        try {
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding(StandardCharsets.UTF_8.displayName());
            response.getWriter().println(JSON.toJSONString(XmResultJson.fail(errcode, errorMessage)));
            response.flushBuffer();
        } catch (IOException e) {
            log.error("[{}] 导出excel错误信息输出失败", modelName, e);
        }
    }
}
